home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CD World 1998 January
/
CD World - Ocak 1998.iso
/
misc
/
dbase55
/
disk7
/
extern.pak
/
DBFILE.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1996-01-05
|
23KB
|
863 lines
//****************************************************************************
//
// FILE: DBFile.cpp
//
// WRITTEN BY: Keimpe
//
// DATE: 1/94
//
// UPDATED: 5/95
//
// REVISION: $Revision: 2.12 $
//
// VERSION: Visual dBASE
//
// DESCRIPTION:
//
// Main source file for dbfile, a Visual dBASE example.
//
// This example implements a series of textfile manipulation routines. These
// routines are accessible from Visual dBASE through the EXTERN system.
// A Textfile object on the Visual dBASE side initiates the contact by creating
// a corresponding C++ Textfile object through a call to TFinit which
// returns a pointer to that C++ object to the Visual dBASE object. After that
// the Visual dBASE object can call into this DLL for specific routines. The
// C++ routine calls back to the Visual dBASE object through DBase->GetThis()
// to get the this pointer of the corresponding C++ object and calls
// the wanted memberfunction of that object.
//
// Right now the available routines are:
// close - closes the current file if any.
// eof - helps detect EOF.
// error - tells if an error was encountered.
// fieldseparator - sets the field separator.
// filter - sets the filter string.
// geterror - returns the last error encountered if any.
// getfield - gets the next field of the current record.
// getline - gets the next line.
// getrec - gets the next record.
// lineseparator - sets the line separator.
// open - opens a file.
// release - releases the C++ object.
//
// A line is ended by a lineseparator or EOF. If you add a '+' char
// to the char you send to "SetLineSeparator" it is assumed that
// the lineseparator is one or more char's of that lineseparator.
// A rec is a line that contains fields. Getrec sets up the fields
// and returns the number of fields in the current rec. A field is
// surrounded by one or more field separators including as special
// cases the start and the end of a line.
// If the filter is set a getline or getrec will search till
// a line is found that contains the filter string as a substring.
//
// The file dbfile.h will give a quick idea of how the C++ class is
// set up. The file dbfile.prg will tell you how things work on the
// Visual dBASE side. The file dbasevar.h deals with the basic classes
// that ease the communication between C++ and Visual dBASE.
//
//****************************************************************************
// Get the header.
#include "dbfile.h"
// If you want to test the TextFile C++ class under DOS, define DOSTEST.
// This will isolate the class from any Windows and Visual dBASE details and
// allows this file to be compiled and linked with DOS programs.
#ifndef DOSTEST
//============================================================================
//
// You can create an instance of class CSession in DBaseInitInstance()
// and set it up. In subsequent call backs, a pointer to this instance
// can be retrieved with a call to GetSession(). Data that is kept on
// a per-instance basis (DBaseVars for example) should be kept in the
// session object.
//
//============================================================================
class CSession {
public:
// Local objects.
CSession(){
// Initialize objects local to this session.
}
~CSession(){
// Destroy objects local to this session.
}
// Memberfunctions to use local objects.
};
extern "C" {
//============================================================================
//
// Usual DLL functions.
//
//============================================================================
int FAR PASCAL LibMain(HINSTANCE, WORD, WORD, LPSTR){
return 1;
}
int CALLBACK WEP(int /*nParam*/){return(1);}
//
// This function is called every time an Instance of Visual dBASE loads the DLL.
// If for some reason the DLL determines that it cannot load, it can return
// an error. If the DLL loads properly it should return DBASE_INIT_OK.
//
int CALLBACK DBaseInitInstance(void){
asm int 3; // break point for the debugger.
DBase()->SetSession(new CSession());
//
// Possible Error return Values
//
//return DBASE_INIT_NO_MULTI_INSTANCE;
//return DBASE_INIT_ERROR;
return DBASE_INIT_OK;
}
//
// DBaseExitInstance() is called when an instance of Visual dBASE terminates or
// manually unloads a DLL through the RELEASE DLL command. This give the
// DLL a chance to free any resources associated with a running instance.
//
void CALLBACK DBaseExitInstance(void){
}
// End of DOSTEST define
#endif
//============================================================================
//
// Implementation of memberfunctions of the TextFile class.
//
//============================================================================
// Some common defines.
#define LINE 0
#define REC 1
//
// AddToText( char*, char * ).
//
// Helper function to append a char string to the Text string. The
// Text buffer grows as needed to fit the largest rec or line
// encountered in the file.
//
// Returns 1 on success, 0 on error.
//
BOOL TextFile::AddToText( char * Begin, char * End ) {
unsigned int Len;
char SaveIt = *End;
// Zero terminate the incoming string.
*End = 0x0;
Len = strlen( Begin );
// See if we need more room.
if( (TextLen + Len) > MaxTextLen ) {
char * NewText;
#ifdef NO_EXCEPTIONS
MaxTextLen = TextLen + Len;
NewText = new char[ MaxTextLen + 1 ];
if( NewText != NULL ) {
strcpy( NewText, Text );
delete [] Text;
Text = NewText;
}
else {
// Must be something wrong.
Text[0] = 0x0;
TextLen = 0;
*End = SaveIt;
return 0;
}
#else // NO_EXCEPTIONS
try {
MaxTextLen = TextLen + Len;
NewText = new char[ MaxTextLen + 1 ];
strcpy( NewText, Text );
delete [] Text;
Text = NewText;
}
catch(...) {
// Must be something wrong.
Text[0] = 0x0;
TextLen = 0;
*End = SaveIt;
return 0;
}
#endif // NO_EXCEPTIONS
}
// Now simply copy it in.
strcpy( Text + TextLen, Begin );
TextLen += Len;
// Restore.
*End = SaveIt;
// Return success.
return 1;
}
//
// Close().
//
// Closes the file and sets Handle to 0.
//
// Returns 1 on success, 0 on error.
//
BOOL TextFile::Close() {
if( Handle != 0 )
if( close( Handle ) != 0 )
return 0;
Handle = 0;
// return success.
return 1;
}
//
// GetField( int ).
//
// This returns a pointer to the field specified by the WhichField
// parameter. The fields are contained inside a rec separated by
// single fieldseparators. When you send a field away, you simply insert
// a zero at the end of that field. Next time you come back here you
// put that separator back which you can always find as the first char
// of the Text buffer.
//
// Returns pointer to field, or pointer to empty string in case of error.
//
char * TextFile::GetField( int WhichField ) {
char *FieldStart = Text + 1, *FieldEnd = Text + 1, Separator = Text[0];
// First see if we have to reinsert the end of a previous
// field we sent away.
if( SaveFieldEndPtr != NULL ) {
*SaveFieldEndPtr = Text[0];
SaveFieldEndPtr = NULL;
}
// Check if we have the field at all.
if( WhichField <= 0 || WhichField > NumberOfFields )
return "";
// Find the field in the Text buffer.
while( --WhichField > 0 ) {
FieldStart = strchr( FieldStart, Separator );
if( FieldStart == NULL ) {
strcpy( ErrorString, "Record corrupted" );
return "";
}
FieldStart++;
}
// Find the end and mark it.
FieldEnd = strchr( FieldStart, Separator );
if( FieldEnd != NULL ) {
SaveFieldEndPtr = FieldEnd;
*FieldEnd = 0x0;
}
// Send it away.
return FieldStart;
}
//
// Open( char* ).
//
// If a file was already open, that file is closed before the
// requested file is opened. Handle is set to the file.
//
// Returns 1 on success, 0 on error.
//
BOOL TextFile::Open( char *FileToOpen ) {
// Is there already a file open.
// If so, close it. Check for proper return.
if( Handle != 0 )
if( close( Handle ) != 0 )
return 0;
// Now open the file requested. Check for valid pointer return.
if( ( Handle = open( FileToOpen, O_RDONLY | O_TEXT ) ) == -1 )
return 0;
// Start a new errorstring. Set up the Buffer.
ErrorString[ 0 ] = 0x0;
BufPtr = Buffer;
Buffer[ 0 ] = 0x0;
// Return success.
return 1;
}
//
// ReadNextItem( char ).
//
// Reads the next item, either a line or a rec as indicated by the
// LineOrRec parameter, into the Text character buffer. The file
// is buffer by buffer read into Buffer. Buffer is scanned for
// separators and EOF. After a field separator is found we handle
// field details if we're reading a rec. After we find a line separator
// the BufPtr is set to after the separator for the next readitem.
// The Buffer is flushed into the Text buffer after each field and
// line or when the Buffer runs out. At the end a complete next
// line or rec will be in the Text buffer.
//
// Returns Text on success, "" on error.
//
char * TextFile::ReadNextItem( char LineOrRec ) {
char Done, Found = 0, StartOfRec;
unsigned int NumberOfBytes;
char BothSeparators[3] = { LineSeparator, 0x0, 0x0 };
char * SepPtr = NULL, SepFound = 0x0;
// In error state?
if( Error() )
return "";
// Are we in business already?
if( Handle == 0 ) {
strcpy( ErrorString, "No file open\n" );
return "";
}
// If we're scanning for a rec, we need the fieldseparator.
if( LineOrRec == REC )
BothSeparators[1] = FieldSeparator;
// Keep reading till we found the next item.
while( !Found ) {
// Set up for the search and initialize Text.
Done = 0;
Text[0] = 0x0;
TextLen = 0;
NumberOfFields = 0;
SaveFieldEndPtr = NULL;
StartOfRec = (LineOrRec == REC );
// Read and scan till we find a separator or EOF.
while( !Done ) {
// Did we run out of chars in the Buffer.
if( *BufPtr == 0x0 ) {
// Read in a buffer full.
NumberOfBytes = (unsigned int) read( Handle,
Buffer, BUFFERLENGTH );
// Check for EOF or error.
if( NumberOfBytes == (unsigned int) -1 ||
NumberOfBytes == 0 ) {
if( NumberOfBytes != 0 ) {
strcpy( ErrorString, "Error Reading\n" );
return ""; // Error.
}
Done = 1; // EOF.
// If there's nothing in the textbuffer we
// can't find a next item.
if( Text[0] == 0x0 ) {
Found = 1;
}
// But if there is something check if we have
// the last field of the file that we haven't
// counted yet.
else if( LineOrRec == REC && SepFound == 0x0 )
NumberOfFields++;
continue;
}
// Set up for the search.
BufPtr = Buffer;
*( BufPtr + NumberOfBytes ) = 0x0;
CopyPtr = Buffer;
}
// If we scan for a REC, first eat any fieldseparators
// at the beginning. If we run out of Buffer, fall
// through and read more. We also put the current
// fieldseparator as the first char of the Text.
if( StartOfRec == 1 ) {
while( *BufPtr == FieldSeparator )
BufPtr++;
if( *BufPtr == 0x0 )
continue;
StartOfRec = 0;
Text[ 0 ] = FieldSeparator;
TextLen++;
CopyPtr = BufPtr;
}
// Look for the next separator IF we're not already
// inside a separator.
if( SepFound == 0x0 ) {
// Cruise till we find a separator.
SepPtr = strpbrk( BufPtr, BothSeparators );
// If we found a separator.
if( SepPtr != NULL ) {
// Mark the end.
BufPtr = SepPtr;
SepFound = *SepPtr;
// If we are looking for fields.
if( LineOrRec == REC ) {
// Increase number of fields. Send one
// fieldseparator to text as well IF
// we found a fieldseparator.
NumberOfFields++;
if( SepFound == FieldSeparator )
BufPtr++;
}
}
else { // We did not find a separator. Adjust BufPtr.
BufPtr += strlen( BufPtr );
}
// Add what we have to Text.
if( AddToText( CopyPtr, BufPtr ) == 0 ) {
// Error occured.
strcpy( ErrorString, "Line/Field too long" );
return "";
}
}
// If we found a separator ( or are inside a separator ).
if( SepFound != 0x0 ) {
// If we found a lineseparator and only singles of
// those are allowed, simply skip one and continue.
if( SepFound == LineSeparator && PlusLineSeparator == 0 ) {
BufPtr++;
SepFound = 0x0;
Done = 1;
CopyPtr = BufPtr;
continue;
}
// Now we scan till we run out of separators.
while( *BufPtr == SepFound )
BufPtr++;
// If we found the end of the buffer, fall
// through and continue reading.
if( *BufPtr == 0x0 )
continue;
// Special case for when we found a lineseparator
// after a bunch of fieldseparators. Means we have
// to fall through and scan for the end of the rec.
if( *BufPtr == LineSeparator ) {
SepFound = LineSeparator;
continue;
}
// We're done IF we were looking for lineseparator(s).
if( SepFound == LineSeparator )
Done = 1;
// Either way, we're done with the separator found.
// And we can adjust copyptr.
SepFound = 0x0;
CopyPtr = BufPtr;
}
}
// Now see if a filter is in place.
if( Filter[0] != 0x0 ) {
if( strstr( Text, Filter ) != NULL )
Found = 1;
}
else {
// Otherwise we have found what we wanted.
Found = 1;
}
}
// Return success.
return Text;
}
//
// SetFilter( char* ).
//
// Sets the Filter char string. ReadNextItem only returns the next
// item IF it contains the Filter string as a substring.
// Can be called with SetFilter( "" ) to set the filter to nothing.
//
// Returns 1 on success. 0 on error.
//
BOOL TextFile::SetFilter( char *InFilter ) {
unsigned int Len = strlen( InFilter );
// If there's enough room, copy it in and leave.
if( Len <= FilterLen ) {
strcpy( Filter, InFilter );
return 1;
}
// Not enough room, start with a clean slate.
delete [] Filter;
// Create some room.
#ifdef NO_EXCEPTIONS
Filter = new char [ Len + 1 ];
if( Filter != NULL ){
FilterLen = Len;
}
else {
// Guess not.
strcpy( ErrorString, "Filter too long" );
FilterLen = 0;
return 0;
}
#else // NO_EXCEPTIONS
try {
Filter = new char [ Len + 1 ];
FilterLen = Len;
}
catch(...) {
// Guess not.
strcpy( ErrorString, "Filter too long" );
FilterLen = 0;
return 0;
}
#endif // NO_EXCEPTIONS
strcpy( Filter, InFilter );
// Success.
return 1;
}
//
// SetFieldSeparator( char * ).
//
// Sets the fieldseparator char.
//
// Returns 1 on success, 0 on error.
//
BOOL TextFile::SetFieldSeparator( char *Separator ) {
// If nothing there, ignore, but return error.
if( Separator == NULL )
return 0;
// Set the FieldSeparator.
FieldSeparator = *Separator;
// We did it.
return 1;
}
//
// SetLineSeparator( char * ).
//
// Sets the lineseparator char. If the second char of the incoming
// string is a '+' it is assumed that the separator is multiples of
// the lineseparator char.
//
// ASSUMES a string is sent over, not a single character!!
//
// Returns 1 on success, 0 on error.
//
BOOL TextFile::SetLineSeparator( char *Separator ) {
// If nothing there, ignore, but return error.
if( Separator == NULL )
return 0;
// Set the LineSeparator and PlusLineSeparator if needed.
LineSeparator = *Separator;
if( *(Separator+1) == '+' )
PlusLineSeparator = 1;
else
PlusLineSeparator = 0;
// We did it.
return 1;
}
// If you want to test the C++ class under DOS, define DOSTEST.
#ifndef DOSTEST
//============================================================================
//
// TextFile routines called from the Visual dBASE side.
//
//============================================================================
//
// GetCPlusPlusThis().
//
// Helper function to get the C++ this pointer out of the Visual dBASE object.
//
// Returns a TextFile pointer.
//
TextFile* GetCPlusPlusThis() {
// Local vars. Get the this of the Visual dBASE object.
DVar CThis, DBaseThis( DBase()->GetThis() );
// Get the this of the corresponding C++ object.
DBaseThis->Property( "MYSTRUCT", CThis );
return (TextFile*)(CThis->Long());
}
//
// TFclose().
//
// Closes the file.
//
// Returns 1 on success, 0 on error.
//
BOOL _export _pascal TFclose() {
return GetCPlusPlusThis()->Close();
}
//
// TFeof().
//
// Returns 1 on EOF, 0 otherwise.
//
BOOL _export _pascal TFeof() {
return GetCPlusPlusThis()->Eof();
}
//
// TFerror().
//
// Returns 1 on error set, 0 otherwise.
//
BOOL _export _pascal TFerror() {
return GetCPlusPlusThis()->Error();
}
//
// TFfilter( char* ).
//
// Sets the filter.
//
// Returns 1 on success, 0 on error.
//
BOOL _export _pascal TFfilter( char * Filter ) {
return GetCPlusPlusThis()->SetFilter( Filter );
}
//
// TFfieldseparator( char* ).
//
// Sets the fieldseparator char.
//
// Returns 1 on success, 0 on error.
//
BOOL _export _pascal TFfieldseparator( char *InString ) {
return GetCPlusPlusThis()->SetFieldSeparator( InString );
}
//
// TFgeterror().
//
// Returns pointer to the errorstring of the object.
//
char * _export _pascal TFgeterror() {
return GetCPlusPlusThis()->GetErrorString();
}
//
// TFgetfield( int ).
//
// Returna a pointer to the field as indicated by the parameter.
//
// Returns pointer to a field on success, "" on error.
//
char * _export _pascal TFgetfield( int WhichField ) {
return GetCPlusPlusThis()->GetField( WhichField );
}
//
// TFgetrec()
//
// Returns # of fields in the next rec on success, 0 on error or EOF.
//
int _export _pascal TFgetrec() {
TextFile * TFptr = GetCPlusPlusThis();
if( TFptr->ReadNextItem( REC ) != NULL )
return TFptr->GetNumberOfFields();
else
return 0;
}
//
// TFgetline().
//
// Returns pointer to the next line on success, "" on error or on EOF.
//
char * _export _pascal TFgetline() {
return GetCPlusPlusThis()->ReadNextItem( LINE );
}
//
// TFinit().
//
// Initialization routine called from Visual dBASE when the Visual dBASE
// textfile object gets created.
//
// Return pointer (cast to a long) to a C++ TextFile object on success,
// 0 on error.
//
long _export _pascal TFinit() {
TextFile * ThisTextFile;
#ifdef NO_EXCEPTIONS
// Get the this of the CTextFile to be used.
ThisTextFile = new TextFile;
if( ThisTextFile != NULL ) {
// Return pointer cast to a long.
return (long) ThisTextFile;
}
else {
// Failure.
return 0L;
}
#else // NO_EXCEPTIONS
try {
// Get the this of the CTextFile to be used.
ThisTextFile = new TextFile;
// Return pointer cast to a long.
return (long) ThisTextFile;
}
catch( ... ) {
// Failure.
return 0L;
}
#endif // NO_EXCEPTIONS
}
//
// TFlineseparator( char* ).
//
// Sets the line separator char.
//
// Returns 1 on success, 0 on error.
//
BOOL _export _pascal TFlineseparator( char *InString ) {
return GetCPlusPlusThis()->SetLineSeparator( InString );
}
//
// TFopen( char* ).
//
// Opens a new file.
//
// Returns 1 on sucess, 0 on error.
//
BOOL _export _pascal TFopen( char * FileToOpen ) {
return GetCPlusPlusThis()->Open( FileToOpen );
}
//
// TFrelease().
//
// Releases the C++ TextFile object.
//
// Returns 1 on sucess, 0 on error.
//
void _export _pascal TFrelease() {
delete GetCPlusPlusThis();
}
} // extern "C"
// End of DOSTEST define
#endif